// The MIT License (MIT)
// Copyright (c) 2011 Akiyoshi Ochiai
// 
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to 
// deal in the Software without restriction, including without limitation the 
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is 
// furnished to do so, subject to the following conditions:
// 
// The above copyright notice and this permission notice shall be included in 
// all copies or substantial portions of the Software.
// 
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
// 
#ifndef SPEECH_RECOG_JULIUS_JULIUS_DRIVER_H_
#define SPEECH_RECOG_JULIUS_JULIUS_DRIVER_H_

#include <cstdlib>
#include <string>
#include <vector>
#include <sstream>

#include <boost/noncopyable.hpp>
#include <boost/function.hpp>

#include <julius/juliuslib.h>

namespace speech_recog_julius {

class RecognizedWord {
 public:
  RecognizedWord() : word_(), confidence_(0.0f) {}

  std::string const& word() const { return word_; }
  void set_word(std::string const& word) { word_ = word; }
  float confidence() const { return confidence_; }
  void set_confidence(float confidence) { confidence_ = confidence; }
 private:
  std::string word_;
  float confidence_;
};

typedef std::vector<RecognizedWord> WordVector;


class RecognizedSentence {
 public:
  RecognizedSentence()
      : words_(),
        score_(0.0f),
        acoustic_model_score_(0.0f),
        language_model_score_(0.0f) {}

  std::string JoinWords() const;
  void AddWord(std::string const& word, float confidence);

  WordVector const& words() const { return words_; }
  float score() const { return score_; }
  void set_score(float score) { score_ = score; }
  float acoustic_model_score() const { return acoustic_model_score_; }
  void set_acoustic_model_score(float acoustic_model_score) { 
    acoustic_model_score_ = acoustic_model_score;
  }
  float language_model_score() const { return language_model_score_; }
  void set_language_model_score(float language_model_score) { 
    language_model_score_ = language_model_score;
  }
 private:
  int num_words;
  WordVector words_;
  float score_;
  float acoustic_model_score_;
  float language_model_score_;
};

typedef std::vector<RecognizedSentence> NBestSentences;

inline std::string RecognizedSentence::JoinWords() const {
  std::stringstream buffer;
  for (WordVector::const_iterator it = words_.begin();
       it != words_.end();
       ++it) {
    buffer << it->word();
  }
  return buffer.str();
}


inline void RecognizedSentence::AddWord(std::string const& word,
                                        float confidence) {
  RecognizedWord elem;
  elem.set_word(word);
  elem.set_confidence(confidence);
  words_.push_back(elem);
}


class JuliusEventHandler {
 public:
  virtual ~JuliusEventHandler() {}
  virtual void OnSpeechReady() = 0;
  virtual void OnRecognitionSuccess(NBestSentences const& sentences) = 0;
  virtual void OnRecognitionFailure(std::string const& message) = 0;
  virtual void OnError() = 0; // Must have a no-throw guarantee.
};


class JuliusDriver : private boost::noncopyable {
 public:

  explicit JuliusDriver(std::string const& config_path);
  ~JuliusDriver();

  void FireSpeechReady();
  void FireRecognitionSuccess(NBestSentences const& sentences);
  void FireRecognitionFailure(std::string const& message);
  void FireError();

  /// Start recognion loop. This function blocks until calling Shutdown().
  /// @throw std::runtime_error
  /// @throw std::logic_error
  void RunLoop();

  /// Call this to stop recognition loop.
  /// @nothrow 
  void Shutdown();

  void RegisterHandler(JuliusEventHandler* handler) { handler_ = handler; }
 private:
  JuliusEventHandler* handler_;
  ::Recog* recog_;
};

} // end namespace speech_recog_julius

#endif//SPEECH_RECOG_JULIUS_JULIUS_DRIVER_H_

